﻿using BarTender;
using IntoStationSystem.Db;
using IntoStationSystem.Global;
using Newtonsoft.Json.Linq;
using S7.Net;
using S7.Net.Types;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace IntoStationSystem.PLC
{
    /// <summary>
    /// PLC掉线后，需要能够重连，同时提示PLC当前状态
    /// </summary>
    public class Service
    {
        public Plc plc;
        /// <summary>
        /// 软件关闭，程序释放
        /// </summary>
        public bool IsDisposed = false;
        public bool IsConnected;
        private PlcData plcData = new PlcData();

        /// <summary>
        /// plc ip
        /// </summary>
        private string ip = Fields.PlcIp;
        /// <summary>
        /// plc data length
        /// </summary>
        private int dataLen = Fields.DataLen;
        /// <summary>
        /// plc读取的db地址
        /// </summary>
        private int dbAddr = Fields.DbAddr;
        /// <summary>
        /// plc read collected data length
        /// </summary>
        private int getDataLen = Fields.GetDataLen;
        /// <summary>
        /// plc读取采集数据的地址
        /// </summary>
        private int getDbAddr = Fields.GetDbAddr;
        /// <summary>
        /// 写给plc的数据
        /// </summary>
        private int setDataLen = Fields.GetDataLen;
        /// <summary>
        /// 写给plc数据的地址
        /// </summary>
        private int setDbAddr = Fields.GetDbAddr;
        /// <summary>
        /// plc数据缓存
        /// </summary>
        private byte[] _datas;
        /// <summary>
        /// 采集数据
        /// </summary>
        private byte[] _collectionDatas;
        private Stopwatch dog = new Stopwatch();

        /// <summary>
        /// 锁
        /// </summary>
        private object locker = new object();
        /// <summary>
        /// 锁
        /// </summary>
        private object locker1 = new object();
        /// <summary>
        /// 锁
        /// </summary>
        private object locker2 = new object();

        /// <summary>
        /// 锁
        /// </summary>
        private object locker3 = new object();

        public delegate void StatusChangedHandle(PlcData status);
        public StatusChangedHandle StatusChangedEvent;
        private void OnStatusChanged()
        {
            if (StatusChangedEvent != null)
            {
                StatusChangedEvent.Invoke(plcData);
            }
        }

        public Service()
        {
            Connect();
        }

        private void Connect()
        {
            plc = new Plc(CpuType.S71200, ip, 0, 1);
            //plc线程
            Task.Run(ConnectPlc);
            //数据读取线程
            Task.Run(GetPlcData);
        }

        /// <summary>
        /// 映射偏移量和长度
        /// </summary>
        /// <param name="name"></param>
        /// <param name="offset"></param>
        /// <param name="len"></param>
        public byte[] Map(string name, byte[] data)
        {
            int offset = Convert.ToInt32(Fields.Map[name].First);
            int len = Convert.ToInt32(Fields.Map[name].Second);
            return data.Skip(offset).Take(len).ToArray();
        }
        /// <summary>
        /// 连接PLC
        /// </summary>
        private void ConnectPlc()
        {
            while (!IsDisposed)
            {
                try
                {

                  
                    //PLC未连接时，连接plc
                    if (!plc.IsConnected)
                    {
                        plc.Open();
                       
                    }
                    else
                    {
                        
                    }
                }
                catch
                {

                }
                Thread.Sleep(1000);
            }
        }
        /// <summary>
        /// 获取PLC数据
        /// </summary>
        private void GetPlcData()
        {
            while (true)
            {
                try
                {
                    IsConnected = plc.IsConnected;
                    if (IsConnected)
                    {
                      

                        _datas = plc.ReadBytes(DataType.DataBlock, dbAddr, 0, dataLen);
                        //采集数据
                        _collectionDatas =plc.ReadBytes(DataType.DataBlock, getDbAddr, 0, getDataLen);

                        plcData.PageId = Int.FromByteArray(Map("PageId", _datas));
                        plcData.PageIdFeedBack = Int.FromByteArray(Map("PageIdFeedBack", _datas));
                        plcData.LocalModelId = Int.FromByteArray(Map("LocalModelId", _datas));
                        plcData.ProduceModel = Int.FromByteArray(Map("SelectModelId", _datas));
                        plcData.ProduceCount = DInt.FromByteArray(Map("TotalProduction", _datas));
                        plcData.OkCount = DInt.FromByteArray(Map("OkQuantity", _datas));
                        plcData.NgCount = DInt.FromByteArray(Map("NgQuantity", _datas));
                        plcData.BeatTime = DInt.FromByteArray(Map("CycleTime", _datas));
                        plcData.RunTime = DInt.FromByteArray(Map("RunTime", _datas));
                        plcData.CraftId = S7.Net.Types.String.FromByteArray(Map("CraftId", _datas)).ToString().Trim();
                        plcData.PartId = S7.Net.Types.String.FromByteArray(Map("PartId", _datas)).ToString().Trim();
                        plcData.AmendId = S7.Net.Types.String.FromByteArray(Map("AmendId", _datas)).ToString().Trim();
                        plcData.SysBit = new BitArray(Map("SysBit", _datas));
                        plcData.SetButton = new BitArray(Map("SetButton", _datas));
                        plcData.PulseButton = new BitArray(Map("PulseButton", _datas));
                        plcData.Manual = new BitArray(Map("Manual", _datas));
                        plcData.Security = S7.Net.Types.Byte.FromByteArray(Map("Security", _datas));
                        plcData.Equipment = S7.Net.Types.Byte.FromByteArray(Map("Equipment", _datas));
                        plcData.Material = S7.Net.Types.Byte.FromByteArray(Map("Material", _datas));
                        plcData.Quality = S7.Net.Types.Byte.FromByteArray(Map("Quality", _datas));
                        plcData.Personnel = S7.Net.Types.Byte.FromByteArray(Map("Personnel", _datas));
                        //配置设备运行状态，步骤提示2. 预留
                        plcData.Step = Int.FromByteArray(Map("Step", _datas)); 

                        //配置参数设置 
                        plcData.SetParam = Real.ToArray(Map("SetParam", _datas));
                        //配置参数显示  
                        plcData.ShowParam = Real.ToArray(Map("ShowParam", _datas));
                        //暂时停用
                        plcData.SetId = Int.FromByteArray(Map("SetId", _datas));
                        //暂时停用
                        plcData.InPut = Word.FromByteArray(Map("InPut", _datas));
                        //暂时停用
                        plcData.OutPut = Word.FromByteArray(Map("OutPut", _datas));
                        //配置提示信息
                        plcData.Prompt = new BitArray(Map("Prompt", _datas));
                        //配置提示信息
                        plcData.Waring = new BitArray(Map("Waring", _datas));
                        //配置提示信息
                        plcData.Alarm = new BitArray(Map("Alarm", _datas));
                        plcData.RfidWrite = new BitArray(Map("RfidWrite", _datas));
                        plcData.RfidRead = new BitArray(Map("RfidRead", _datas));
                        plcData.RfidPartByte1 = S7.Net.Types.Byte.FromByteArray(Map("RfidPartByte1", _datas));
                        plcData.RfidPartByte2 = S7.Net.Types.Byte.FromByteArray(Map("RfidPartByte2", _datas));
                        plcData.RfidPartByte3 = S7.Net.Types.Byte.FromByteArray(Map("RfidPartByte3", _datas));
                        plcData.RfidPartByte4 = S7.Net.Types.Byte.FromByteArray(Map("RfidPartByte4", _datas));
                        plcData.LableByte = Map("LableByte", _datas);
                        plcData.RfidGroup1 = Map("RfidGroup1", _datas);
                        plcData.RfidGroup2 = Map("RfidGroup2", _datas);
                        for (int i = 0; i < plcData.Groups.Length; i++)
                        {
                            plcData.Groups[i].Write = new BitArray(Map($"Group{i + 1}Write", _datas));
                            plcData.Groups[i].Read = new BitArray(Map($"Group{i + 1}Read", _datas));
                            plcData.Groups[i].Byte = Map($"Group{i + 1}Read", _datas);
                            plcData.Groups[i].SetReal = Real.ToArray(Map($"Group{i + 1}SetReal", _datas));
                            plcData.Groups[i].ShowReal = Real.ToArray(Map($"Group{i + 1}ShowReal", _datas));
                        }
                        plcData.SetNumber = Int.FromByteArray(Map("SetNumber", _datas));
                        plcData.Number = Int.FromByteArray(Map("Number", _datas));
                        plcData.Ratio = Int.FromByteArray(Map("Ratio", _datas));
                        plcData.Inching = DInt.FromByteArray(Map("Inching", _datas));
                        plcData.CurrentPOPU = DInt.FromByteArray(Map("CurrentPOPU", _datas));
                        plcData.CurrentPOMM = Real.FromByteArray(Map("CurrentPOMM", _datas));
                        plcData.NewWarning = Int.FromByteArray(Map("NewWarning", _datas));
                        plcData.Fault = Int.FromByteArray(Map("Fault", _datas));
                        plcData.ForwardBack = new BitArray(Map("ForwardBack", _datas));
                        plcData.State = new BitArray(Map("State", _datas));
                        plcData.Collection = Map("CollectIdentification", _datas);
                        plcData.Reads = Map("Reads", _datas);
                        plcData.Writes = Map("Writes", _datas);

                        //采集数据转换
                        plcData.GetFloats = Real.ToArray(Map("GetReals", _collectionDatas));
                        plcData.GetBytes = Map("GetBytes", _collectionDatas);
                    }
                    //通知更新
                    OnStatusChanged();
                    Thread.Sleep(200);
                }
                catch (Exception ex)
                {
                    DbHelper.Log(ex.InnerException.ToString());
                }
            }
        }
       
        /// <summary>
        /// 复位按钮
        /// </summary>
        public  void ResetButton(int offset)
        {
           
                    if (IsConnected)
            {


                             //Log("复位写入开始1");
                            plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["PulseButton"].First}.{offset}", true);
                          //Log("复位写入结束1");
                           //DbHelper.LogTip($"复位:offset:{offset} value:True");
                            Thread.Sleep(300);
                //Log("复位写入开始2");
                plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["PulseButton"].First}.{offset}", false);
                //Log("复位写入结束2");
                //DbHelper.LogTip($"复位:offset:{offset} value:False");

            }
               
        }

        private static void Log(string message)
        {
            using (StreamWriter writer = new StreamWriter("D:/messge.txt", append: true))
            {
                string logMessage = $"{System.DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}";
                writer.WriteLine(logMessage);
            }
        }

        /// <summary>
        /// 解绑写入
        /// </summary>
        /// <param name="offset"></param>
        public void ResetPartWrite()
        {
            //心跳写入
            plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["RstCode"].First}.0", 1);
        }
 



        /// <summary>
        /// 置位按钮
        /// </summary>
        public void SettingButton(int offset, bool value)
        {

                if (IsConnected)
                {
                    plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["SetButton"].First}.{offset}", value);
                    //DbHelper.LogTip($"置位:offset:{offset} value:{value}");
                }
          
        }
        /// <summary>
        /// 强制NG按钮
        /// </summary>
        /// <param name="offset"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void ForceNGButton(int offset,bool val)
        {
           
                if (IsConnected)
                {
                    plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["SetButton"].First}.{offset}", val);
                    DbHelper.LogTip($"强制NG:offset:{offset} value:{val}");

                }
          
        }


        /// <summary>
        /// 本地远程按钮
        /// </summary>
        /// <param name="offset"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void LocalRemoteButton(int offset, bool val)
        {
           
             //plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["SetButton"].First}.{offset}", val);
                //DbHelper.LogTip($"强制NG:offset:{offset} value:{val}");

           
        }

        /// <summary>
        /// 空托盘模式
        /// </summary>
        /// <param name="offset"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void EmptyTrayButton(int offset, bool val)
        {
           
                if (IsConnected)
                {
                    plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["SetButton"].First}.{offset}", val);
                    DbHelper.LogTip($"空托盘模式:offset:{offset} value:{val}");

                }
           
        }


        /// <summary>
        /// 空托盘模式
        /// </summary>
        /// <param name="offset"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void MuteButton(int offset, bool val)
        {

            if (IsConnected)
            {
                plc.Write($"DB{Fields.DbAddr}.DBX{Fields.Map["SetButton"].First}.{offset}", val);
                DbHelper.LogTip($"消音模式:offset:{offset} value:{val}");

            }

        }
        


        /// <summary>
        /// 回复给PLC不同的采集信号
        /// </summary>
        public void ResponseCollection(int offset)
        {
            if (IsConnected)
            {
                string addr = $"DB{Fields.DbAddr}.DBB{Fields.Map["CollectIdentification"].First + offset}";
                plc.Write(addr, (byte)2);
                DbHelper.LogTip($"采集:offset:{offset} value: 2");
            }
        }

       
        /// <summary>
        /// 写入参数
        /// </summary>
        public void WriteData(int offset, float[] datas, int dbAddr)
        {
            if (IsConnected)
            {
                try
                {
                    byte[] bytes = Real.ToByteArray(datas);
                    plc.WriteBytes(DataType.DataBlock, dbAddr, offset, bytes);
                }
                catch (Exception)
                {

                   
                }
                
            }
        }
        /// <summary>
        /// 写入参数
        /// </summary>
        public void WriteData(int offset, byte[] datas, int dbAddr)
        {
            if (IsConnected)
            {
                try
                {
                    plc.WriteBytes(DataType.DataBlock, dbAddr, offset, datas);
                }
                catch (Exception)
                {

                   
                }
               
            }
        }

        public void WriteBool(int offset, int bitOffset, bool value)
        {
            if (IsConnected)
            {
                try
                {
                    plc.WriteBit(DataType.DataBlock, dbAddr, offset, bitOffset, value);
                }
                catch (Exception)
                {

                   
                }
               
            }
        }

        public void WriteData(int offset, float[] datas)
        {
            if (IsConnected)
            {
                try
                {
                    byte[] bytes = Real.ToByteArray(datas);
                    plc.WriteBytes(DataType.DataBlock, dbAddr, offset, bytes);
                }
                catch (Exception)
                {

                    throw;
                }
                
            }

        }
        public void WriteData(int offset, bool[] datas)
        {
            if (IsConnected)
            {
                byte[] bytes = BoolsToBytes(datas);
                plc.WriteBytes(DataType.DataBlock, dbAddr, offset, bytes);
            }

        }


        private byte[] BoolsToBytes(bool[] value)
        {
            int byteLength = value.Length % 8 == 0 ? value.Length / 8 : value.Length / 8 + 1;

            byte[] result = new byte[byteLength];

            for (int i = 0; i < result.Length; i++)
            {

                int total = value.Length < 8 * (i + 1) ? value.Length - 8 * i : 8;

                for (int j = 0; j < total; j++)
                {
                    result[i] = SetBitValue(result[i], j, value[8 * i + j]);
                }
            }
            return result;
        }

        private byte SetBitValue(byte src, int bit, bool value)
        {
            return value ? (byte)(src | (byte)Math.Pow(2, bit)) : (byte)(src & ~(byte)Math.Pow(2, bit));
        }

        public void WriteData(int offset, byte[] datas)
        {
            if (IsConnected)
            {
                plc.WriteBytes(DataType.DataBlock, dbAddr, offset, datas);
            }
        }

        public void WriteMesData(int offset, byte[] datas)
        {
            if (IsConnected)
            {
                plc.WriteBytes(DataType.DataBlock, getDbAddr, offset, datas);
            }
        }
        /// <summary>
        /// 读取plc的输入输出信号
        /// </summary>
        /// <param name="offset">偏移量</param>
        /// <param name="Addr">起始地址</param>
        /// <param name="StartByteAddr"></param>
        /// <param name="DataTypes"></param>
        /// <param name="Lenth"></param>
        /// <returns></returns>
        public bool[]ReadBoolArray(int Addr,int StartByteAddr,string DataTypes,int Lenth)
        {
            List<bool> result = new List<bool>();
            if (IsConnected)
            {
                switch (DataTypes)
                {
                   case "I":
                        byte[] Input = plc.ReadBytes(DataType.Input, Addr, StartByteAddr, Lenth);
                        result = thinger.DataConvertLib.BitLib.GetBitArrayFromByteArray(Input).ToList();
                        break;
                    case "Q":
                        byte[] Output = plc.ReadBytes(DataType.Output, Addr, StartByteAddr, Lenth);
                        result = thinger.DataConvertLib.BitLib.GetBitArrayFromByteArray(Output).ToList();
                        break;
                }
               
            }
            return result.ToArray();
        }
        public void SetPageID(short pageId)
        {
            byte[] data = Int.ToByteArray(pageId);
            if (IsConnected)
            {
                plc.WriteBytes(DataType.DataBlock, dbAddr, 0, data);
            }
        }

        /// <summary>
        /// 设置屏蔽设备
        /// </summary>
        public void SetShieldDev()
        {

        }

        
    }
}
